//
//  CycBLEManager.m
//  Cycling
//
//  Created by cjg.mini on 2023/3/9.
//

#import "CycBLEManager.h"

NSString * const CYCBLEManagerErrorDomain = @"CYCBLEManagerErrorDomain";

/// 每包数据的大小 - 因硬件所采用的蓝牙版本不同新车型可以发送182字节，旧车型只能发送20字节。可通过maximumWriteValueLengthForType获取后设置。
const NSInteger kCYCBLEPacketSize = 20;

@interface CycBLEManager()<CBCentralManagerDelegate, CBPeripheralDelegate>

@property (nonatomic, strong, readwrite) NSMutableArray *discoveredPeripherals;         //!< 发现的设备数组
@property (nonatomic, assign, readwrite) CYCBLEManagerState state;                      //!< 蓝牙的状态
@property (nonatomic, strong) CBPeripheral *connectingPeripheral;                       //!< 正在连接的设备
@property (nonatomic, strong) CBCharacteristic *dataoutCharacteristics;                 //!< dataout特征
@property (nonatomic, strong) CBCharacteristic *datainCharacteristics;                  //!< datain特征
@property (nonatomic, strong) CBCharacteristic *datafcCharacteristic;                   //!< datafc特征
@property (nonatomic, weak) id<CYCBLEManagerDelegate> delegate;                         //!< 代理 （暂不用)
@property (nonatomic, strong) CBCentralManager *centralManager;                         //!< 中心管理
@property (nonatomic, copy) CYCBLEManagerScanSuccessBlock scanSuccessBlock;             //!< 搜索成功block
@property (nonatomic, copy) CYCBLEManagerConnectionSuccessBlock connectionSuccessBlock; //!< 连接成功block
@property (nonatomic, copy) CYCBLEManagerConnectionFailureBlock connectionFailureBlock; //!< 连接失败block
@property (nonatomic, strong) NSTimer *timer;                                           //!< 连接超时time
@property (nonatomic, strong) NSMutableDictionary *stateChangeBlock;                    //!< 状态改变block
@property (nonatomic, assign) NSUInteger packetLength;

@end

@implementation CycBLEManager

-(NSMutableArray<CBPeripheral *> *)discoveredPeripherals {
    if(!_discoveredPeripherals){
        _discoveredPeripherals = [NSMutableArray arrayWithCapacity:1];
    }
    return _discoveredPeripherals;
}

+ (instancetype)sharedManager {
    static CycBLEManager *_sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedInstance = [[CycBLEManager alloc] init];
    });
    return _sharedInstance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionShowPowerAlertKey: @NO}];
    }
    return self;
}

#pragma mark 开始扫描
- (void)startScanWithSuccess:(CYCBLEManagerScanSuccessBlock)success failure:(CYCBLEManagerScanFailureBlock)failure {
    NSLog(@"CYCBLEManager= 开始搜索%ld",(long)self.state);
    
    if (self.state != CYCBLEManagerStateReady && self.state != CYCBLEManagerStateIdle) {
        NSError *error = [NSError errorWithDomain:CYCBLEManagerErrorDomain code:CYCBLEManagerErrorCodeWrongState userInfo:nil];
        failure(error);
        [self stopScan];
        return;
    }
    
    [self.discoveredPeripherals removeAllObjects];
    self.scanSuccessBlock = success;
    if (self.centralManager.state == CBCentralManagerStatePoweredOn) {
        self.state = CYCBLEManagerStateScanning;
        
        NSArray *services = @[[CBUUID UUIDWithString:PERIPHERAL_SERVICE_UUID]];
        /// 开始搜索 -- 不指定服务
        [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey: @YES}];
        
    } else if (self.centralManager.state == CBCentralManagerStatePoweredOff) {
        NSError *error = [NSError errorWithDomain:CYCBLEManagerErrorDomain code:CYCBLEManagerErrorCodeBluetoothPoweredOff userInfo:nil];
        failure(error);
        [self stopScan];
    } else {
        NSError *error = [NSError errorWithDomain:CYCBLEManagerErrorDomain code:CYCBLEManagerErrorCodeBluetoothUnavailable userInfo:nil];
        failure(error);
        [self stopScan];
    }
}

#pragma mark 停止搜索
- (void)stopScan {
    NSLog(@"CYCBLEManager= 停止搜索%ld",(long)self.state);
    if (self.state == CYCBLEManagerStateScanning) {
        self.state = CYCBLEManagerStateIdle;
    }
    [self.centralManager stopScan];
    self.scanSuccessBlock = nil;
}

#pragma mark 连接设备
- (void)connectPeripheral:(CBPeripheral *)peripheral success:(CYCBLEManagerConnectionSuccessBlock)success failure:(CYCBLEManagerConnectionFailureBlock)failure {
    
    self.state = CYCBLEManagerStateConnecting;
    self.connectionSuccessBlock = success;
    self.connectionFailureBlock = failure;
    self.connectingPeripheral = peripheral;
    [self.centralManager connectPeripheral:peripheral options:nil];
    
    if (self.timer) {
        [self.timer invalidate];
        self.timer = nil;
    }
    self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(connectionTimeout) userInfo:nil repeats:false];
}

#pragma mark 连接超时操作
- (void)connectionTimeout {
    if (self.timer) {
        [self.timer invalidate];
        self.timer = nil;
    }
    [self cancelConnection:self.connectingPeripheral];
    self.state = CYCBLEManagerStateIdle;
    NSError *error = [NSError errorWithDomain:CYCBLEManagerErrorDomain code:CYCBLEManagerErrorCodeConnectionTimeout userInfo:@{NSLocalizedDescriptionKey:@"Connection timed out"}];
    self.connectionFailureBlock(error);
    self.connectingPeripheral = nil;
    self.connectionFailureBlock = nil;
    self.connectionSuccessBlock = nil;
}

#pragma mark 添加状态改变block
- (void)addStateChangeBlock:(CYCBLEManagerStateChangeBlock)block forKey:(NSString *)key {
    if (!_stateChangeBlock) {
        _stateChangeBlock = [NSMutableDictionary dictionaryWithCapacity:1];
    }
    [_stateChangeBlock setObject:[block copy] forKey:key];
}

#pragma mark 删除状态改变block
- (void)removeStateChangeBlockForKey:(NSString *)key {
    [_stateChangeBlock removeObjectForKey:key];
}

#pragma mark 取消连接设备
- (void)cancelConnection:(CBPeripheral *)peripheral {
    NSLog(@"CYCBLEManager= 取消连接设备%ld",(long)self.state);
    if (self.state == CYCBLEManagerStateConnecting ||
        self.state == CYCBLEManagerStateConnected ||
        self.state == CYCBLEManagerStateSubscribed) {
        [self.centralManager cancelPeripheralConnection:peripheral];
    } else {
        NSLog(@"CYCBLEManager=想要断开连接，但是并不是连接中到已订阅状态。。。");
    }
}

#pragma mark 断开连接
- (void)disconnect {
    NSLog(@"CYCBLEManager= 断开连接设备%ld",(long)self.state);
    if (self.timer) {
        [self.timer invalidate];
        self.timer = nil;
    }
    self.state = CYCBLEManagerStateIdle;
    
    if (self.connectingPeripheral) {
        [self.centralManager cancelPeripheralConnection:self.connectingPeripheral];
    }
    if (self.connectedPeripheral) {
        [self.centralManager cancelPeripheralConnection:self.connectedPeripheral];
    }
}

#pragma mark 发送数据
- (void)sendData:(NSData *)data {
    if (data == nil || data.length == 0) {
        return;
    }
    
    NSInteger sendedIndex = 0;
    for (int i = 0; i < data.length; i++) {
        if (sendedIndex < data.length) {
            NSInteger size = data.length - sendedIndex;
            if (size > self.packetLength) {
                size = self.packetLength;  /// 每次发20字节或超出20字节，根据硬件判断
            }
            NSRange range = NSMakeRange(sendedIndex, size);
            NSData *chunk = [data subdataWithRange:range];
            if (!self.datainCharacteristics) {
                return;
            }
            [self.connectedPeripheral writeValue:chunk forCharacteristic:self.datainCharacteristics type:CBCharacteristicWriteWithResponse];
            sendedIndex += size;
            NSLog(@"CYCBLEManager=发送蓝牙数据 ==== :%@",chunk);
        } else {
            break;
        }
    }
}

#pragma mark 处理发现外设
- (void)handleDiscoveredPeripheral:(CBPeripheral *)discoveredPeripheral RSSI:(NSNumber *)RSSI {
    
    BOOL contains = NO;
    for (CBPeripheral *peripheral in self.discoveredPeripherals) {
        if ([discoveredPeripheral.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]) {
            contains = YES;
            return;
        }
    }
    
    if (!contains) {
        if (![self.discoveredPeripherals containsObject:discoveredPeripheral] && discoveredPeripheral.name.length) {
            NSLog(@"CYCBLEManager=发现外设 :%@----RSSI:%@", discoveredPeripheral,RSSI);
            [self.discoveredPeripherals addObject:discoveredPeripheral];
            if (self.scanSuccessBlock) {
                self.scanSuccessBlock(self.discoveredPeripherals.copy);
            }
        }
    }
}

#pragma mark 设置蓝牙状态，回调蓝牙状态修改block
- (void)setState:(CYCBLEManagerState)state {
    _state = state;
    NSString *s = nil;
    switch (state) {
        case CYCBLEManagerStateUnavailable:
            s = @"CYCBLEManagerStateUnavailable";
            break;
        case CYCBLEManagerStateReady:
            s = @"CYCBLEManagerStateReady";
            break;
        case CYCBLEManagerStateIdle:
            s = @"CYCBLEManagerStateIdle";
            break;
        case CYCBLEManagerStateScanning:
            s = @"CYCBLEManagerStateScanning";
            break;
        case CYCBLEManagerStateConnecting:
            s = @"CYCBLEManagerStateConnecting";
            break;
        case CYCBLEManagerStateConnected:
            s = @"CYCBLEManagerStateConnected";
            break;
        case CYCBLEManagerStateSubscribed:
            s = @"CYCBLEManagerStateSubscribed";
            break;
        default:
            break;
    }
    NSLog(@"CYCBLEManager=蓝牙状态: %@", s);
    for (CYCBLEManagerStateChangeBlock block in _stateChangeBlock.allValues) {
        block(state);
    }
}

#pragma mark 已经连接
- (BOOL)connected {
    return _state == CYCBLEManagerStateSubscribed;
}

#pragma mark - CentralManagerDelegate
#pragma mark 手机蓝牙状态改变回调
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    NSLog(@"CYCBLEManager=手机蓝牙状态%ld",(long)central.state);
    switch (central.state) {
        case CBCentralManagerStatePoweredOn:
            if (self.state == CYCBLEManagerStateUnavailable) {
                self.state = CYCBLEManagerStateReady;
            } else {
                NSLog(@"CYCBLEManager=未知蓝牙状态。。。");
            }
            break;
        default:
            self.state = CYCBLEManagerStateUnavailable;
            break;
    }
}

#pragma mark 发现外设回调
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
    [self handleDiscoveredPeripheral:peripheral RSSI:RSSI];
}

#pragma mark 连接上外设回调
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    peripheral.delegate = self;
    self.state = CYCBLEManagerStateConnected;
    // 连接上后设置默认最大传输子节数为20.
    self.packetLength = kCYCBLEPacketSize;
    NSLog(@"CYCBLEManager============= 扫描服务");
    // 连接外设后发现该外设的服务
    [peripheral discoverServices:@[[CBUUID UUIDWithString:PERIPHERAL_SERVICE_UUID]]];
}

#pragma mark 连接外设失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    NSLog(@"CYCBLEManager=连接失败回掉 :%@ %@", peripheral.name, [error localizedDescription]);
    if (self.state == CYCBLEManagerStateConnecting) {
        if (self.timer) {
            [self.timer invalidate];
            self.timer = nil;
        }
        self.state = CYCBLEManagerStateIdle;
        NSError *error = [NSError errorWithDomain:CYCBLEManagerErrorDomain code:CYCBLEManagerErrorCodeConnectionFailed userInfo:@{NSLocalizedDescriptionKey:@"Connection Failed"}];
        self.connectionFailureBlock(error);
        self.connectionFailureBlock = nil;
        self.connectionSuccessBlock = nil;
    } else {
        NSLog(@"CYCBLEManager=连接失败，但是状态不是连接中");
    }
}

#pragma mark 设备连接断开了
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    NSLog(@"CYCBLEManager= 蓝牙断开回掉 :%@ === %ld", error,(long)self.state);
    //    [self.centralManager connectPeripheral:peripheral options:nil]; /// 断开自动重连
    
    if (self.state == CYCBLEManagerStateConnected) {
        if (self.connectionFailureBlock) {
            self.state = CYCBLEManagerStateIdle;
            NSError *error = [NSError errorWithDomain:CYCBLEManagerErrorDomain code:CYCBLEManagerErrorCodeAutoDisconnected userInfo:@{NSLocalizedDescriptionKey:@"AutoDisconnected"}];
            [self.timer invalidate];
            self.connectionFailureBlock(error);
            self.connectionFailureBlock = nil;
            self.connectionSuccessBlock = nil;
        } else {
            self.state = CYCBLEManagerStateReady;
            NSLog(@"CYCBLEManager=断开连接，但是不是已连接状态");
        }
    } else if (self.state == CYCBLEManagerStateSubscribed) {
        self.state = CYCBLEManagerStateReady;
    } else {
        NSLog(@"CYCBLEManager=断开连接，但是不是已连接状态---");
        self.state = CYCBLEManagerStateReady;
    }
}

#pragma mark - PeriphralDelegate
#pragma mark 发现服务回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    NSLog(@"CYCBLEManager=设备所有的服务 %@",peripheral.services);
    if (self.state != CYCBLEManagerStateConnected) {
        NSLog(@"CYCBLEManager=发现服务，但是不是已连接状态。。。");
        return;
    }
    for (CBService *service in peripheral.services) {
        [peripheral discoverCharacteristics:nil forService:service];
    }
}

#pragma mark 发现特征回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    NSLog(@"CYCBLEManager=设备所有的特征 %@",service.characteristics);
    if (self.state != CYCBLEManagerStateConnected) {
        NSLog(@"CYCBLEManager=发现特征值，但是不是已连接状态。。。");
        return;
    }
    for (CBCharacteristic *characteristic in service.characteristics) {
        
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:PERIPHERAL_DATAFC_CHAR_UUID]]) {
            // 订阅data fc特征:
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            break;
        }
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:PERIPHERAL_DATAOUT_CHAR_UUID]]) {
            // 订阅data out特征:
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            break;
        }

        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:PERIPHERAL_DATAIN_CHAR_UUID]]) {
            // 保存写特征：
            self.datainCharacteristics = characteristic;
            break;
        }
    }
}

#pragma mark 更新特征回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (self.state != CYCBLEManagerStateConnected) {
        NSLog(@"CYCBLEManager=订阅回调，但是状态不对...");
        return;
    }
    if (error) {
        NSLog(@"CYCBLEManager=更新特征回调 错误 - %@", [error localizedDescription]);
        return;
    }
    if (self.timer) {
        [self.timer invalidate];
        self.timer = nil;
    }
    
    if (characteristic.UUID == [CBUUID UUIDWithString:PERIPHERAL_DATAOUT_CHAR_UUID]) {
        if (characteristic.isNotifying) {
            NSLog(@"CYCBLEManager= 订阅特征成功 -- ");
            if (self.connectionSuccessBlock) {
                self.state = CYCBLEManagerStateSubscribed;
                self.connectedPeripheral = peripheral;
                
                //判断当前外设的蓝牙版本所支持的最大传输子节数。 -- 暂不处理。
                NSUInteger datalenth = [peripheral maximumWriteValueLengthForType:(CBCharacteristicWriteWithoutResponse)];
                if (datalenth > 20) {
                    self.packetLength = datalenth - 10;
                }
                
                self.connectionSuccessBlock(peripheral);
                self.connectionFailureBlock = nil;
                self.connectionSuccessBlock = nil;
            } else {
                NSLog(@"CYCBLEManager=成功订阅的回调 -- 但是 self.connectionSuccessBlock && self.macAdress 不成立");
            }
        } else {
            NSLog(@"CYCBLEManager= 订阅失败 CYCBLEManager cannot subscribe %@", characteristic);
            if (self.connectionFailureBlock) {
                self.state = CYCBLEManagerStateIdle;
                NSError *e = [NSError errorWithDomain:CYCBLEManagerErrorDomain code:CYCBLEManagerErrorCodeCannotSubscribe userInfo:error.userInfo];
                self.connectionFailureBlock(e);
                self.connectionFailureBlock = nil;
                self.connectionSuccessBlock = nil;
            } else {
                NSLog(@"CYCBLEManager= 连接失败前已经超时。。。");
            }
        }
    }
}

#pragma mark 收到特征的响应回调  ---------- 收到数据
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"CYCBLEManager=  接收数据出错");
        return;
    }
    NSLog(@"CYCBLEManager蓝牙原始数据回调 ==========  %@",characteristic.value);
    if([characteristic.UUID.UUIDString isEqualToString:PERIPHERAL_DATAOUT_CHAR_UUID]){
        self.receiveDataoutBlock(characteristic.value);
    }
    if([characteristic.UUID.UUIDString isEqualToString:PERIPHERAL_DATAFC_CHAR_UUID]){
        self.receiveDatafcBlock(characteristic.value);
    }
}

@end
